/** @file bounding.h
    @brief Bounding geometry */

#ifndef G3_BOUNDING_H
#define G3_BOUNDING_H

#include "log.h"

#include "m_vector.h"
#include "m_matrix.h"
#include "color.h"

namespace G3 {

/** @class RECT
    @brief A simple rectangle class */
template <class T>
class RECT {
	public:
        /**
            @brief Constructor that sets the rectangle coordinates
            @param[in] aLeft The left coordinate
            @param[in] aTop The top coordinate
            @param[in] aRight The right coordinate
            @param[in] aBottom The bottom coordinate
        */
		RECT (T aLeft = 0, T aTop = 0, T aRight = 0, T aBottom = 0);
        /**
            @brief Constructor that sets the rectangle coordinates
            @param[in] Min Reference to the minimum corner
            @param[in] Max Reference to the maximum corner
        */
		RECT (const VEC2<T> &Min, const VEC2<T> &Max);

        /** The left coordinate */
		T Left;
		/** The top coordinate */
		T Top;
		/** The right coordinate */
		T Right;
		/** The bottom coordinate */
		T Bottom;

        /**
            @brief Assignment operator
            @param[in] Rect Reference to the rectangle to be copied
            @return Reference to the freshly assigned rectangle
        */
		RECT<T>& operator= (const RECT<T> &Rect);

        /**
            @brief Comparison operator
            @param[in] Rect Reference to the rectangle to be compared with this
            @return True in case they are equal, false otherwise
        */
		bool operator== (const RECT<T> &Rect) const;

        /**
            @brief Coordinate by index
            @param[in] ID The index of the coordinate
            @return The coordinate
        */
		T operator[] (const int ID) const;

        /**
            @brief Scale operator
            @param[in] f Reference to the scale factor
            @return Reference to the freshly scaled rectangle
        */
		RECT<T>& operator*= (const T &f);

        /**
            @brief Expands the rect by another one
            @param[in] Rect Reference to the other rectangle
        */
		void Expand (const RECT<T> &Rect);

        /**
            @brief Checks if this rectangle contains another rectangle
            @param[in] Rect Reference to the rectangle to be checked
        */
		bool Contains (const RECT<T> &Rect) const;

        /**
            @brief Checks if the rectangle is vertical
            @return True if it is, otherwise false
        */
		inline bool IsVertical () const { return (Bottom - Top) > (Right - Left); }

        /**
            @brief Checks if the rectangle is horizontal
            @return True if it is, otherwise false
        */
		inline bool IsHorizontal () const { return (Bottom - Top) < (Right - Left); }

        /**
            @brief Returns the width of the rectangle
            @return Width
        */
		inline T GetWidth () const { return Right - Left; }

        /**
            @brief Returns the height of the rectangle
            @return Height
        */
		inline T GetHeight () const { return Bottom - Top; }

        /**
            @brief Swaps rectangle axis
        */
		void SwapAxis ();

        /**
            @brief Resets the coordinates of this rectangle
        */
		void Reset ();

        /**
            @brief Calculates the area of this rectangle
            @return Area
        */
		T Area () const;

        /**
            @brief Dumps the rectangle into the log
            \todo Needs template specialization to get this thing to work
        */
		void Dump (LOG *aLog) const;
};

/** @class AABB3
    @brief Axis-aligned bounding box class */
template <class T>
class AABB3 {
	public:
        /**
            @brief Constructor that sets the coordinates
            @param[in] MinX Minimum X-coordinate
            @param[in] MinY Minimum Y-coordinate
            @param[in] MinZ Minimum Z-coordinate
            @param[in] MaxX Maximum X-coordinate
            @param[in] MaxY Maximum Y-coordinate
            @param[in] MaxZ Maximum Z-coordinate
        */
		AABB3 (T MinX = 0, T MinY = 0, T MinZ = 0, T MaxX = 0, T MaxY = 0, T MaxZ = 0);
        /**
            @brief Constructor that sets the coordinates
            @param[in] Min Minimum corner
            @param[in] Max Maximum corner
        */
		AABB3 (const VEC3<T> &Min, const VEC3<T> &Max);

        /** The minimum corner */
		VEC3<T> Minimum;
		/** The maximum corner */
		VEC3<T> Maximum;

        /**
            @brief Assignment operator
            @param[in] b Reference to the bounding box to be copied
            @return The freshly copied bounding box
        */
		AABB3<T>& operator= (const AABB3<T> &b);

        /**
            @brief Comparison operator
            @param[in] b Reference to the bounding box to be compared to this one
            @return True if both are equal and false if only one of them is =)
        */
		bool operator== (const AABB3<T> &b) const;

        /**
            @brief Scale operator
            @param[in] f Reference to the scale factor
            @return Reference to the freshly scaled bounding box
        */
		AABB3<T>& operator*= (const T &f);

        /**
            @brief Expands the bounding box by another bounding box
            @param[in] b Reference to the other bounding box
        */
		void Expand (const AABB3<T> &b);
        /**
            @brief Expands the bounding box by another bounding box
            @param[in] A Reference to the minimum corner of other bounding box
            @param[in] B Reference to the maximum corner of other bounding box
        */
		void Expand (const VEC3<T> &A, const VEC3<T> &B);
        /**
            @brief Expands the bounding box by a vertex
            @param[in] A Reference to the vertex
        */
		void Expand (const VEC3<T> &A);

        /**
            @brief Is another AABB in this one ?
            @param[in] b Reference to the other AABB
            @return True means yes and false no
        */
		bool Overlaps (const AABB3<T> &b) const;
        /**
            @brief Is another AABB in this one ?
            @param[in] A Reference to the minimum corner of the other AABB
            @param[in] B Reference to the maximum corner of the other AABB
            @return True in case it is, false if it is not
        */
		bool Overlaps (const VEC3<T> &A, const VEC3<T> &B) const;

        /**
            @brief Finds the closest point to <i>p</i> on the bounding box
            @param[in] p Reference to the point to which the closest point is to be found
            @param[out] q Reference to the closest point
        */
        void ClosestPoint (const VEC3<T> &p, VEC3<T> &q) const;

        /**
            @brief Calculates the distance of a point from the bounding box
            @param[in] p Reference to the point
            @return Distance between the point and this bounding box
        */
        double Distance (const VEC3<T> &p) const;

        /**
            @brief Calculates the volume of this bounding box
            @return The volume
        */
        T Volume () const;

        /**
            @brief Finds the longest axis
            @return 0 = X, 1 = Y, 2 = Z
        */
        byte LongestAxis() const;

        /**
            @brief Checks if a point is inside
            @param[in] p Reference to the point to be checked
            @return True if it is, false otherwise
        */
        bool PointInside( G3::VEC3<T> &p ) const;

        /**
            @brief Updates the AABB by a rotation matrix
            @param[in] M Reference to a 3 x 3 rotation matrix
            \todo What was that t argument ?
        */
        void Update (const MATRIX<3, T> &M, const VEC3<T> &t);

        /**
            @brief Renders the bounding box in wireframe
            @param[in] Color The color for the wires
        */
		void Render (const COL3F Color = G3_GREEN_3F) const;
};

template <class T>
class BSPH3 {
	public:
        BSPH3 ();
		BSPH3 (const VEC3<T> &Pos, T R);

		VEC3<T> Position;
		T Radius;

		BSPH3<T>& operator= (const BSPH3<T> &b);
		bool operator== (const BSPH3<T> &b) const;

		BSPH3<T>& operator*= (const T &f);

//==============================================================================
// Creates a bounding sphere from a bounding box
//==============================================================================
		void Create (const VEC3<T> &Min, const VEC3<T> &Max);

//==============================================================================
// Expands the bounding sphere
//==============================================================================
		void Expand (const BSPH3<T> &b);
		void Expand (const VEC3<T> &A, const VEC3<T> &B);
		void Expand (const VEC3<T> &A);

        /**
            @brief Checks if a point is inside
            @param[in] p Reference to the point to be checked
            @return True if it is, false otherwise
        */
        bool PointInside( G3::VEC3<T> &p ) const;

//==============================================================================
// Checks if the bounding sphere overlaps another one
//==============================================================================
		bool Overlaps (const BSPH3<T> &b) const;
};

template <class T>
class BCAP3 {
    public:
        BCAP3 (const VEC3<T> &a, const VEC3<T> &b, const T &radius);

        VEC3<T> Start;
        VEC3<T> End;

        T Radius;

        BCAP3<T>& operator= (const BCAP3<T> &b);
		bool operator== (const BCAP3<T> &b) const;

		BCAP3<T>& operator*= (const T &f);

//==============================================================================
// Checks if a capsule overlaps another one
//==============================================================================
		bool Overlaps (const BCAP3<T> &c) const;
};

template <class T>
bool TestSphereCapsule (const BSPH3<T> &s, const BCAP3<T> &c);

//==============================================================================
// Explicit template instantiation
//==============================================================================
extern template class RECT<int>;
extern template class RECT<long>;
extern template class RECT<float>;
extern template class RECT<double>;

extern template class AABB3<float>;
extern template class AABB3<double>;

extern template class BSPH3<float>;
extern template class BSPH3<double>;

extern template class BCAP3<float>;
extern template class BCAP3<double>;

//==============================================================================
// Types
//==============================================================================
typedef RECT<int>		RECTI;
typedef RECT<long>		RECTL;
typedef RECT<float>		RECTF;
typedef RECT<double>	RECTD;

typedef AABB3<float>	AABB3F;
typedef AABB3<double>	AABB3D;

typedef BSPH3<float>	BSPH3F;
typedef BSPH3<double>	BSPH3D;

}

#endif //G3_BOUNDING_H
